﻿using System;
using System.IO;
using System.Text.RegularExpressions;

namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles;

internal static class CssRelativePath
{
    private static readonly Regex _rxUrl = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\s*\)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

    public static string Adjust(
        string cssFileContents,
        string absoluteInputFilePath,
        string absoluteOutputPath)
    {
        var matches = _rxUrl.Matches(cssFileContents);

        if (matches.Count <= 0)
        {
            return cssFileContents;
        }

        var cssDirectoryPath = Path.GetDirectoryName(absoluteInputFilePath)!;

        foreach (Match match in matches)
        {
            string quoteDelimiter = match.Groups[1].Value; //url('') vs url("")
            string relativePathToCss = match.Groups[2].Value;

            // Ignore root relative references
            if (relativePathToCss.StartsWith("/", StringComparison.Ordinal))
                continue;

            //prevent query string from causing error
            var pathAndQuery = relativePathToCss.Split(new[] { '?' }, 2, StringSplitOptions.RemoveEmptyEntries);
            var pathOnly = pathAndQuery[0];
            var queryOnly = pathAndQuery.Length == 2 ? pathAndQuery[1] : string.Empty;

            string absolutePath = GetAbsolutePath(cssDirectoryPath, pathOnly);
            string serverRelativeUrl = MakeRelative(absoluteOutputPath, absolutePath);

            if (!string.IsNullOrEmpty(queryOnly))
                serverRelativeUrl += "?" + queryOnly;

            string replace = string.Format("url({0}{1}{0})", quoteDelimiter, serverRelativeUrl);

            cssFileContents = cssFileContents.Replace(match.Groups[0].Value, replace);
        }

        return cssFileContents;
    }

    private static string GetAbsolutePath(string cssFilePath, string pathOnly)
    {
        return Path.GetFullPath(Path.Combine(cssFilePath, pathOnly));
    }

    private static readonly string _protocol = "file:///";
    private static string MakeRelative(string baseFile, string file)
    {
        if (string.IsNullOrEmpty(file))
            return file;

        Uri baseUri = new Uri(_protocol + baseFile, UriKind.RelativeOrAbsolute);
        Uri fileUri = new Uri(_protocol + file, UriKind.RelativeOrAbsolute);

        if (baseUri.IsAbsoluteUri)
        {
            return Uri.UnescapeDataString(baseUri.MakeRelativeUri(fileUri).ToString());
        }
        else
        {
            return baseUri.ToString();
        }
    }
}
